#include "Fiber.h"

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <math.h>

#include <vtkPoints.h>
#include <vtkPolyData.h>
#include <vtkGenericDataObjectWriter.h>
#include <vtkCellArray.h>
#include <vtkCellData.h>

CFiber::CFiber(void)
{
	m_fCount = 0;
	m_bCount = 0;

    //m_pFColor = NULL;
    //m_pBColor = NULL;
}


CFiber::~CFiber(void)
{
	m_fCount = 0;
	m_bCount = 0;

    //SafeDeleteArray(m_pFColor);
    //SafeDeleteArray(m_pBColor);
}

void CFiber::CreateFiber(const CPoint3F *pF, const CPoint3F *pB, 
	const uint fCount, const uint bCount, const Seed *seed)
{
	memset(m_pF, 0, sizeof(CPoint3F)*MAX_POINTS);
	memset(m_pB, 0, sizeof(CPoint3F)*MAX_POINTS);

	m_fCount = fCount > MAX_POINTS ? MAX_POINTS : fCount;
	m_bCount = bCount > MAX_POINTS ? MAX_POINTS : bCount;;

	if (pF == NULL || pB == NULL || seed == NULL || fCount == 0 || bCount == 0) {
		m_seed.dir = CVector3F(0.0f, 0.0f, 0.0f);
		m_seed.pos = seed->pos;
	} else {
		memcpy(m_pF, pF, sizeof(CPoint3F)*m_fCount);

		memcpy(m_pB, pB, sizeof(CPoint3F)*m_bCount);

		m_seed.dir = seed->dir;
		m_seed.pos = seed->pos;
	}

	ComputeColor();
}


void CFiber::ComputeColor()
{
    if (m_fCount == 0 || m_bCount == 0)
		return;

    //SafeDeleteArray(m_pFColor);
    //SafeDeleteArray(m_pBColor);
    //m_pFColor = new CVector3F[MAX_POINTS];
    //m_pBColor = new CVector3F[MAX_POINTS];

	memset(m_pFColor, 0, sizeof(CVector3F)*MAX_POINTS);
	memset(m_pBColor, 0, sizeof(CVector3F)*MAX_POINTS);

	for (uint i = 0; i < m_fCount; ++i) {
		float r, g, b;
		if (i == 0) {
			r = m_pF[1].m_x - m_pF[0].m_x;
			g = m_pF[1].m_y - m_pF[0].m_y;
			b = m_pF[1].m_z - m_pF[0].m_z;
		} else if (i == m_fCount-1) {
			r = m_pF[i].m_x - m_pF[i-1].m_x;
			g = m_pF[i].m_y - m_pF[i-1].m_y;
			b = m_pF[i].m_z - m_pF[i-1].m_z;
		} else {
			r = m_pF[i+1].m_x - m_pF[i-1].m_x;
			g = m_pF[i+1].m_y - m_pF[i-1].m_y;
			b = m_pF[i+1].m_z - m_pF[i-1].m_z;
		}

		float l = sqrtf(r * r + g * g + b * b);
		m_pFColor[i].m_x = fabs(r / l);
		m_pFColor[i].m_y = fabs(g / l);
		m_pFColor[i].m_z = fabs(b / l);
	}

	for (uint i = 0; i < m_bCount; ++i) {
		float r, g, b;
		if (i == 0) {
			r = m_pB[1].m_x - m_pB[0].m_x;
			g = m_pB[1].m_y - m_pB[0].m_y;
			b = m_pB[1].m_z - m_pB[0].m_z;
		} else if (i == m_bCount-1) {
			r = m_pB[i].m_x - m_pB[i-1].m_x;
			g = m_pB[i].m_y - m_pB[i-1].m_y;
			b = m_pB[i].m_z - m_pB[i-1].m_z;
		} else {
			r = m_pB[i+1].m_x - m_pB[i-1].m_x;
			g = m_pB[i+1].m_y - m_pB[i-1].m_y;
			b = m_pB[i+1].m_z - m_pB[i-1].m_z;
		}

		float l = sqrtf(r * r + g * g + b * b);
		m_pBColor[i].m_x = fabs(r / l);
		m_pBColor[i].m_y = fabs(g / l);
		m_pBColor[i].m_z = fabs(b / l);
	}
   
}

void CFiber::CopyFiber(const CFiber *fiber)
{
	CreateFiber(fiber->m_pF, fiber->m_pB, fiber->m_fCount, fiber->m_bCount, &(fiber->m_seed));

	//ComputeColor();
}



void ReadFibers(const char *confPathname, const char *vertPathname, CFiber **fibers, int &w, int &h)
{
	FILE *confFP = fopen(confPathname, "r");
	FILE *vertFP = fopen(vertPathname, "rb");

	if (confFP == NULL || vertFP == NULL) {
        printf("Cannot open file %s or %s\n", confPathname, vertPathname);
		exit(-1);
    }

	w = h = 0;
	fscanf(confFP, "%d %d", &w, &h);
	*fibers = new CFiber[w*h];

	int count = w * h;

	memset(*fibers, 0, sizeof(CFiber)*count);

	for (int i = 0; i < count; ++i) {
		int id, fnum, bnum;
		CPoint3F dir;
		fscanf(confFP, "%d\n", &id);
		fscanf(confFP, "%d %d\t", &fnum, &bnum);
		fscanf(confFP, "%f %f %f\n", &(dir.m_x), &(dir.m_y), &(dir.m_z));
		CPoint3F *forward = new CPoint3F[fnum];
		CPoint3F *backward = new CPoint3F[bnum];
		fread(forward, sizeof(CPoint3F), fnum, vertFP);
		fread(backward, sizeof(CPoint3F), bnum, vertFP);
		Seed seed;
		seed.dir = dir;
		seed.pos = forward[0];

		(*fibers)[i].CreateFiber(forward, backward, fnum, bnum, &seed);
		(*fibers)[i].ComputeColor();

		delete[] forward;
		delete[] backward;
	}

	fclose(confFP);
	fclose(vertFP);
}

void ReadFibers(const char *confPathname, const char *vertPathname, CFiber ***fibers, int **count, int &w, int &h)
{
	FILE *confFP = fopen(confPathname, "r");
	FILE *vertFP = fopen(vertPathname, "rb");

	if (confFP == NULL || vertFP == NULL) {
        printf("Cannot open file %s or %s\n", confPathname, vertPathname);
		exit(-1);
    }

	w = h = 0;
	fscanf(confFP, "%d %d", &w, &h);
	*fibers = new CFiber*[w*h];
    *count = new int[w*h];

    int size = w * h;

	memset(*fibers, 0, sizeof(CFiber *)*size);
    memset(*count, 0, sizeof(int)*size);

	for (int i = 0; i < size; ++i) {
		int _count;
		fscanf(confFP, "%d\n", &_count);
        
        (*count)[i] = _count;
        (*fibers)[i] = new CFiber[_count];

        for (int j = 0; j < _count; ++j) {
            int fnum, bnum;
            CPoint3F dir;
            fscanf(confFP, "%d %d\t", &fnum, &bnum);
            fscanf(confFP, "%f %f %f\n", &(dir.m_x), &(dir.m_y), &(dir.m_z));
            CPoint3F *forward = new CPoint3F[fnum];
            CPoint3F *backward = new CPoint3F[bnum];
            fread(forward, sizeof(CPoint3F), fnum, vertFP);
            fread(backward, sizeof(CPoint3F), bnum, vertFP);
            Seed seed;
            seed.dir = dir;
            seed.pos = forward[0];
            
            (*fibers)[i][j].CreateFiber(forward, backward, fnum, bnum, &seed);
            //(*fibers)[i].ComputeColor();
            
            delete[] forward;
            delete[] backward;
        }
	}

	fclose(confFP);
	fclose(vertFP);

}

void SaveFibers(const char *confPathname, const char *vertPathname, CFiber *fibers, const int w, const int h)
{
	FILE *confFP = fopen(confPathname, "w");
	FILE *vertFP = fopen(vertPathname, "wb");
	
	if (confFP == NULL || vertFP == NULL)
		exit(-1);

	fprintf(confFP, "%d %d\n", w, h);

	for (int i = 0; i < w*h; ++i) {
		int fnum = fibers[i].m_fCount;
		int bnum = fibers[i].m_bCount;
		CVector3F dir = fibers[i].m_seed.dir;
		fprintf(confFP, "%d\n", 1);
		fprintf(confFP, "%d %d\t", fnum, bnum);
		fprintf(confFP, "%f %f %f\n", dir.m_x, dir.m_y, dir.m_z);

		fwrite(fibers[i].m_pF, sizeof(CPoint3F), fnum, vertFP);
		fwrite(fibers[i].m_pB, sizeof(CPoint3F), bnum, vertFP);
	}

	fclose(confFP);
	fclose(vertFP);
}

void SaveFibers(const char *confPathname, const char *vertPathname, CFiber **fibers, int *count, const int w, const int h)
{
	FILE *confFP = fopen(confPathname, "w");
	FILE *vertFP = fopen(vertPathname, "wb");
	
	if (confFP == NULL || vertFP == NULL)
		exit(-1);

	fprintf(confFP, "%d %d\n", w, h);

	for (int i = 0; i < w*h; ++i) {
        int fiberCount = count[i];
		fprintf(confFP, "%d\n", fiberCount);
        for (int j = 0; j < fiberCount; ++j) {
            int fnum = fibers[i][j].m_fCount;
            int bnum = fibers[i][j].m_bCount;
            CVector3F dir = fibers[i][j].m_seed.dir;
            
            fprintf(confFP, "%d %d\t", fnum, bnum);
            fprintf(confFP, "%f %f %f\n", dir.m_x, dir.m_y, dir.m_z);
            
            fwrite(fibers[i][j].m_pF, sizeof(CPoint3F), fnum, vertFP);
            fwrite(fibers[i][j].m_pB, sizeof(CPoint3F), bnum, vertFP);
        }
	}

	fclose(confFP);
	fclose(vertFP);
}

void ReadConfigureFile(const char *pathname, char ***confPathname, char ***vertPathname, 
	int &w, int &h, int &d, int &scale, float &stepSize, float &maxLength)
{
	FILE *fp = fopen(pathname, "r");
	if (NULL == fp)
		exit(-1);

	w = h = d = 0;
	scale = 1;
	stepSize = maxLength = 0.0f;
	fscanf(fp, "%d %d %d %d %f %f", &w, &h, &d, &scale, &stepSize, &maxLength);

	*confPathname = new char*[d];
	*vertPathname = new char*[d];
	for (int i = 0; i < d; ++i) {
		(*confPathname)[i] = new char[PATHNAME_SIZE];
		(*vertPathname)[i] = new char[PATHNAME_SIZE];
		fscanf(fp, "%s %s\n", (*confPathname)[i], (*vertPathname)[i]);
	}
	fclose(fp);
}

void SaveFibersAsVTK(const char *pathname, CFiber *fibers, const int count)
{
	vtkPoints *pts = vtkPoints::New();
	vtkUnsignedCharArray *colors = vtkUnsignedCharArray::New();
	colors->SetNumberOfComponents(3);
	colors->SetName("Colors");

	for (int i = 0; i < count; ++i) {
		fibers[i].ComputeColor();
	}

	pts->Initialize();
	for (int i = 0; i < count; ++i) {
		for (uint k = 0; k < fibers[i].m_fCount; ++k) {
			//float x = fibers[i].m_pF[k].m_x * 8.0f - 240.0f;
			//float y = fibers[i].m_pF[k].m_z * 8.0f - 440.0f;
			//float z = (fibers[i].m_pF[k].m_y * 8.0f - 600.0f) * 2.0f;

			float x = fibers[i].m_pF[k].m_x * 1.0f;
			float y = fibers[i].m_pF[k].m_y * 1.0f;
			float z = fibers[i].m_pF[k].m_z * 2.0f;

			pts->InsertNextPoint(x, y, z);

			//pts->InsertNextPoint(fibers[i].m_pF[k].m_x, 
			//	fibers[i].m_pF[k].m_y, fibers[i].m_pF[k].m_z);

		}
		for (uint k = 0; k < fibers[i].m_bCount; ++k) {
			//float x = fibers[i].m_pB[k].m_x * 8.0f - 240.0f;
			//float y = fibers[i].m_pB[k].m_z * 8.0f - 440.0f;
			//float z = (fibers[i].m_pB[k].m_y * 8.0f - 600.0f) * 2.0f;

			float x = fibers[i].m_pB[k].m_x * 1.0f;
			float y = fibers[i].m_pB[k].m_y * 1.0f;
			float z = fibers[i].m_pB[k].m_z * 2.0f;

			pts->InsertNextPoint(x, y, z);

			//pts->InsertNextPoint(fibers[i].m_pB[k].m_x, 
			//	fibers[i].m_pB[k].m_y, fibers[i].m_pB[k].m_z);
		}
	}

	vtkPolyData *fibersData = vtkPolyData::New();
	fibersData->Initialize();
	fibersData->Allocate();
	fibersData->SetPoints(pts);
	uint index = 0;
	for (int i = 0; i < count; ++i) {
		for (uint k = 1; k < fibers[i].m_fCount; ++k) {
			vtkIdType connectivity[2]; 
			connectivity[0] = index; 
			connectivity[1] = index + 1; 
			fibersData->InsertNextCell(VTK_LINE,2,connectivity); 
			index++;

			unsigned char color[3];
			color[0] = (unsigned char)(fibers[i].m_pFColor[k].m_x * 255.0f);
			color[1] = (unsigned char)(fibers[i].m_pFColor[k].m_y * 255.0f);
			color[2] = (unsigned char)(fibers[i].m_pFColor[k].m_z * 255.0f);
			colors->InsertNextTupleValue(color);
		}
		index++;
		for (uint k = 1; k < fibers[i].m_bCount; ++k) {
			vtkIdType connectivity[2]; 
			connectivity[0] = index; 
			connectivity[1] = index + 1; 
			fibersData->InsertNextCell(VTK_LINE,2,connectivity); 
			index++;

			unsigned char color[3];
			color[0] = (unsigned char)(fibers[i].m_pBColor[k].m_x * 255.0f);
			color[1] = (unsigned char)(fibers[i].m_pBColor[k].m_y * 255.0f);
			color[2] = (unsigned char)(fibers[i].m_pBColor[k].m_z * 255.0f);
			colors->InsertNextTupleValue(color);
		}
		index++;
	}
	fibersData->GetCellData()->SetScalars(colors);
	fibersData->Update();

	vtkGenericDataObjectWriter* writer = vtkGenericDataObjectWriter::New();
	writer->SetFileName(pathname);
	writer->SetInput(fibersData);          //polydata is my polydata object
	writer->Update();
	writer->Write();
}


void SaveFibersAsVTK(const char *pathname, CFiber **fibers, int *count, const int w, const int h)
{
	vtkPoints *pts = vtkPoints::New();
	vtkUnsignedCharArray *colors = vtkUnsignedCharArray::New();
	colors->SetNumberOfComponents(3);
	colors->SetName("Colors");

	int size = w * h;
	for (int i = 0; i < size; ++i) {
		for (int j = 0; j < count[i]; ++j) {
			fibers[i][j].ComputeColor();
		}
	}

	pts->Initialize();
	for (int i = 0; i < size; ++i) {
		for (int j = 0; j < count[i]; ++j) {
			//int count = fibers[i][j].m_fCount > 2 ? 2 : fibers[i][j].m_fCount;
			uint count = fibers[i][j].m_fCount;
			for (uint k = 0; k < count; ++k) {
				//float x = fibers[i][j].m_pF[k].m_x * 8.0f - 240.0f;
				//float y = fibers[i][j].m_pF[k].m_z * 8.0f - 440.0f;
				//float z = (fibers[i][j].m_pF[k].m_y * 8.0f - 600.0f) * 2.0f;
				float x = fibers[i][j].m_pF[k].m_x;
				float y = fibers[i][j].m_pF[k].m_y;
				float z = fibers[i][j].m_pF[k].m_z;
				pts->InsertNextPoint(x, y, z);

			}
			//count = fibers[i][j].m_bCount > 2 ? 2 : fibers[i][j].m_bCount;
			count = fibers[i][j].m_bCount;
			for (uint k = 0; k < count; ++k) {
				float x = fibers[i][j].m_pB[k].m_x;
				float y = fibers[i][j].m_pB[k].m_y;
				float z = fibers[i][j].m_pB[k].m_z;
				pts->InsertNextPoint(x, y, z);
			}
		}
	}

	vtkPolyData *fibersData = vtkPolyData::New();
	fibersData->Initialize();
	fibersData->Allocate();
	fibersData->SetPoints(pts);
	uint index = 0;
	for (int i = 0; i < size; ++i) {
		for (int j = 0; j < count[i]; ++j) {
			//int count = fibers[i][j].m_fCount > 2 ? 2 : fibers[i][j].m_fCount;
			uint count = fibers[i][j].m_fCount;
			for (uint k = 1; k < count; ++k) {
				vtkIdType connectivity[2]; 
				connectivity[0] = index; 
				connectivity[1] = index + 1; 
				fibersData->InsertNextCell(VTK_LINE,2,connectivity); 
				index++;

				unsigned char color[3];
				color[0] = (unsigned char)(fibers[i][j].m_pFColor[k].m_x * 255.0f);
				color[1] = (unsigned char)(fibers[i][j].m_pFColor[k].m_y * 255.0f);
				color[2] = (unsigned char)(fibers[i][j].m_pFColor[k].m_z * 255.0f);
				colors->InsertNextTupleValue(color);
			}
			index++;
			//count = fibers[i][j].m_bCount > 2 ? 2 : fibers[i][j].m_bCount;
			count = fibers[i][j].m_bCount;
			for (uint k = 1; k < count; ++k) {
				vtkIdType connectivity[2]; 
				connectivity[0] = index; 
				connectivity[1] = index + 1; 
				fibersData->InsertNextCell(VTK_LINE,2,connectivity); 
				index++;

				unsigned char color[3];
				color[0] = (unsigned char)(fibers[i][j].m_pBColor[k].m_x * 255.0f);
				color[1] = (unsigned char)(fibers[i][j].m_pBColor[k].m_y * 255.0f);
				color[2] = (unsigned char)(fibers[i][j].m_pBColor[k].m_z * 255.0f);
				colors->InsertNextTupleValue(color);
			}
			index++;
		}
		
	}
	fibersData->GetCellData()->SetScalars(colors);
	fibersData->Update();

	vtkGenericDataObjectWriter* writer = vtkGenericDataObjectWriter::New();
	writer->SetFileName(pathname);
	writer->SetInput(fibersData);          //polydata is my polydata object
	writer->Update();
	writer->Write();
}
